#include <assert.h>
#include <errno.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>

#include <err.h>
#include <sysexits.h>

#include "util.h"

void *
allot(void **ptr, size_t *s, size_t n, size_t m, size_t d)
{
	void *p;

	assert(ptr != NULL && s != NULL && m > 0);
	if (*ptr == NULL)
		*s = 0;
	assert(SIZE_MAX / m >= d && SIZE_MAX - *s >= d * m);
	if (*s / m <= n) {
		if (d == 0)
			return (NULL);
		if ((p = (*ptr == NULL ? malloc(d * m) :
		    realloc(*ptr, *s + d * m))) == NULL)
			err(EX_OSERR, "allot");
		*ptr = p;
		*s += d * m;
	}
	return (((char *)*ptr) + n * m);
}

void
memshuffle(void *p, size_t n, size_t s)
{
	size_t i;

	assert(p != NULL);
	if (n < 2 || s == 0)
		return;
	assert(SIZE_MAX / n >= s);
	for (i = 0; i < n - 1; i++)
		memswap((char *)p + arc4random_uniform(n - i) * s,
		    (char *)p + i * s, s);
}

void
memswap(void *a, void *b, size_t s)
{
	char buf[sizeof(void *) * 0x10];
	size_t i;

	assert(a != NULL && b != NULL);
	if (a == b)
		return;
	for (i = 0; i + sizeof(buf) <= s; i += sizeof(buf)) {
		(void)memcpy(buf, (char *)a + i, sizeof(buf));
		(void)memcpy((char *)a + i, (char *)b + i, sizeof(buf));
		(void)memcpy((char *)b + i, buf, sizeof(buf));
	}
	if (i < s) {
		(void)memcpy(buf, (char *)a + i, s - i);
		(void)memcpy((char *)a + i, (char *)b + i, s - i);
		(void)memcpy((char *)b + i, buf, s - i);
	}
}
